#ifndef USB_HPP
#define USB_HPP

#include "usb.h"
namespace USBTemp {


#define  CheckValid2(b,reason,line)\
    CheckValid1(b,reason,line)

#define  CheckValid1(b,reason,line)\
    char reason##_at_line_##line[(bool)(b)?0:-1];

#define  CheckValid(b,reason)\
    CheckValid2(b,reason,__LINE__)

class _dummy{};

template <bool> struct STATIC_ASSERT_FAILURE;
template <> struct STATIC_ASSERT_FAILURE<true> {};


template<uint8_t val>
class DescValue8
{
public:
    DescValue8():_v(val){}
    const uint8_t _v;
    enum{v = val};
};

template<uint16_t val>
class DescValue16
{
public:
    DescValue8<(uint8_t)val>            _lo;
    DescValue8<(uint8_t)(val>>8)>       _hi;
};

template<uint32_t val>
class DescValue32
{
public:
    DescValue16<(uint16_t)val>          _lo;
    DescValue16<(uint16_t)(val>>16)>    _hi;
};


template<uint16_t ver>
class UsbVer
{
public:
     DescValue16<ver>            _ver;
};

template<uint8_t devClass, uint8_t subClass, uint8_t protocol>
class DevClass
{
public:
    DescValue8<devClass>            _class;
    DescValue8<subClass>            _subClass;
    DescValue8<protocol>            _protocol;
};

template<uint8_t ep0size>
class PacketSize
{
public:
    DescValue8<ep0size>             _ep0size;
    typedef DescValue8<ep0size>     value_t;
};

template<uint16_t vid, uint16_t pid>
class DevID
{
public:
    DescValue16<vid> _vid;
    DescValue16<pid> _pid;
};

template<uint16_t ver>
class DevVer
{
public:
     DescValue16<ver>            _ver;
};


template<uint8_t iMan, uint8_t iProd, uint8_t iSerial>
class DevSering
{
public:
    DescValue8<iMan>            _iMan;
    DescValue8<iProd>           _iProd;
    DescValue8<iSerial>         _iSerial;
};


template<uint8_t configCount>
class ConfigCount
{
public:
    DescValue8<configCount>            _configCount;
};


template< 
    class _UsbVer = UsbVer<0x200>, 
    class _DevClass = DevClass<0,0,0>, 
    class _PacketSize = PacketSize<64>,
    class _DevID = DevID<0x1985,0x1017>, 
    class _DevVer = DevVer<0x100>, 
    class _DevSering = DevSering<0,0,0>, 
    class _ConfigCount = ConfigCount<1>
    >
class DeviceDesc
{
public:
    DescValue8<LEN_DEVICE>            _len;
    DescValue8<DESC_DEVICE>           _desc;
    _UsbVer                           _usbver;
    _DevClass                         _class;
    _PacketSize                       _packetSize;
    _DevID                            _id;
    _DevVer                           _devver;
    _DevSering                        _string;
    _ConfigCount                      _cfgcnt;
    typedef typename _PacketSize::value_t      ep0size_t;
    
};

template<uint8_t _maxpower>
struct buspower{        enum{ v = 1<<6 ,maxpower = _maxpower}; };
struct selfpower{       enum{ v = 0    ,maxpower = 0}; };
struct remotwakup{      enum{ v = 1<<5 }; };
struct noremotwakup{    enum{ v = 0    }; };


template<class _powermode, class _remotewakeup = noremotwakup>
class Attribute
{
public:
    enum{ v =  (1<<7) | _powermode::v | _remotewakeup::v};
    enum{ maxpower = _powermode::maxpower};
};


/** 
 * End point related templates
 */

template<uint8_t addr>
class epMask
{
public:
    enum{
        epInMask  = addr&0x80 ? (1<<(addr&0xf)) :0 ,
        epOutMask = addr&0x80 ? 0: (1<<(addr&0xf)) ,
    };
};

template<class _EndPointDesc, class _AppendEndPoint = _dummy>
class AppendEndPoint
{
public:
    enum{
        length = _EndPointDesc::length + _AppendEndPoint::length,
        number = 1 + _AppendEndPoint::number,
        epInUsage = epMask<_EndPointDesc::epAddr_t::v>::epInMask | _AppendEndPoint::epInUsage,
        epOutUsage = epMask<_EndPointDesc::epAddr_t::v>::epOutMask  | _AppendEndPoint::epOutUsage,
    };
    _EndPointDesc           _endpointDesc;
    _AppendEndPoint         _appDesc;
    CheckValid( (_AppendEndPoint::epInUsage & epMask<_EndPointDesc::epAddr_t::v>::epInMask) == 0 ,end_point_already_used);
    CheckValid( (_AppendEndPoint::epOutUsage & epMask<_EndPointDesc::epAddr_t::v>::epOutMask) == 0 ,end_point_already_used);
};

template<class _EndPointDesc>
class AppendEndPoint<_EndPointDesc, _dummy>
{
public:
    enum{
        length = _EndPointDesc::length,
        number = 1,
        epInUsage = epMask<_EndPointDesc::epAddr_t::v>::epInMask,
        epOutUsage = epMask<_EndPointDesc::epAddr_t::v>::epOutMask,
    };
    _EndPointDesc           _endpointDesc;

    CheckValid(1,end_point_already_used);
};

template<uint8_t _epAddr>
class EpIntIn
{
public:
    enum{
        epAddr = (_epAddr & 0x7f) | 0x80,
        epType =  EP_INT,
        };
};

template<uint8_t _epAddr>
class EpIntOut
{
public:
    enum{
        epAddr = (_epAddr & 0x7f),
        epType =  EP_INT,
        };
};

template<uint8_t _epAddr>
class EpBulkIn
{
public:
    enum{
        epAddr = (_epAddr & 0x7f) | 0x80,
        epType =  EP_BULK,
        };
};

template<uint8_t _epAddr>
class EpBulkOut
{
public:
    enum{
        epAddr = (_epAddr & 0x7f),
        epType =  EP_BULK,
        };
};


struct IsoSynType_none          { enum {v = 0}; };
struct IsoSynType_asynchronous  { enum {v = 1}; };
struct IsoSynType_adaptive      { enum {v = 2}; };
struct IsoSynType_synchronous   { enum {v = 3}; };

struct IsoUsage_data            { enum {v = 0}; };
struct IsoUsage_feedback        { enum {v = 0}; };
struct IsoUsage_implicitfeed    { enum {v = 0}; };
struct IsoUsage_reserved        { enum {v = 0}; };

template<uint8_t _epAddr, class IsoSynType = IsoSynType_none, class IsoUsage = IsoUsage_data>
class EpIsoIn
{
public:
    enum{
        epAddr = (_epAddr & 0x7f) | 0x80,
        epType =  EP_ISO | (IsoSynType::v<<2) | (IsoUsage::v<<4),
        };
};

template<uint8_t _epAddr, class IsoSynType = IsoSynType_none, class IsoUsage = IsoUsage_data>
class EpIsoOut
{
public:
    enum{
        epAddr = (_epAddr & 0x7f),
        epType =  EP_ISO | (IsoSynType::v<<2) | (IsoUsage::v<<4),
        };
};


template<class _epType, uint16_t maxPacketSize = 64, uint8_t interval = 1>
class EndPointDesc
{
public:
    enum{ length = LEN_ENDPOINT};
    DescValue8<LEN_ENDPOINT>               _len;
    DescValue8<DESC_ENDPOINT>              _desc;
    DescValue8<_epType::epAddr>            _addr;
    DescValue8<_epType::epType>            _attr;
    DescValue16<maxPacketSize>             _maxPacketSize;
    DescValue8<interval>                   _interval;
    typedef   DescValue8<_epType::epAddr>  epAddr_t;
};

template<
    uint8_t index, 
    class _ClassDescriptor, 
    uint8_t iString = 0>
class Interface
{
public:
    enum{length = LEN_INTERFACE + _ClassDescriptor::length};
    enum{ epInUsage = _ClassDescriptor::epInUsage,
          epOutUsage = _ClassDescriptor::epOutUsage,   };
    DescValue8<LEN_INTERFACE>               _len;
    DescValue8<DESC_INTERFACE>              _desc;
    DescValue8<index>                       _index;
    DescValue8<0>                           _alternateSetting;
    DescValue8<_ClassDescriptor::epCnt>     _epCnt;
    DescValue8<_ClassDescriptor::interfaceClass>        _class;
    DescValue8<_ClassDescriptor::interfaceSubClass>     _subClass;
    DescValue8<_ClassDescriptor::interfaceProtocol>     _protocol;
    DescValue8<iString>                                 _iString;
    _ClassDescriptor                        _classDesc;
};

template<
    class _Interface,
    class _AppendInterface = _dummy
     >
class AppendInterface
{
public:
    enum{
        length = _AppendInterface::length + _Interface::length,
        number = 1 + _AppendInterface::number,
        epInUsage = _Interface::epInUsage | _AppendInterface::epInUsage,
        epOutUsage = _Interface::epOutUsage | _AppendInterface::epOutUsage,
    };
    _Interface                  _interfaceDesc;
    _AppendInterface            _appDesc;

    CheckValid( (_AppendInterface::epInUsage & _Interface::epInUsage) == 0 ,end_point_already_used);
    CheckValid( (_AppendInterface::epOutUsage & _Interface::epOutUsage) == 0 ,end_point_already_used);
};

template< class _Interface >
class AppendInterface<_Interface,_dummy>
{
public:
    
    enum{ 
        length = _Interface::length,
        number = 1,
        epInUsage = _Interface::epInUsage,
        epOutUsage = _Interface::epOutUsage,        
    };
    _Interface                  _interfaceDesc;
};


template<
    class       _InterfaceList,
    uint8_t     configValue = 1,
    uint8_t     configString = 0,
    class       _Attribute = Attribute<buspower<100> >
    >
class ConfigDesc
{
    DescValue8<LEN_CONFIG>                              _len;
    DescValue8<DESC_CONFIG>                             _desc;
    DescValue16<_InterfaceList::length+LEN_CONFIG>      _total;
    DescValue8<_InterfaceList::number>                  _number;
    DescValue8<configValue>                             _cfgvalue;
    DescValue8<configString>                            _cfgstring;
    DescValue8<_Attribute::v>                           _attribute;
    DescValue8<_Attribute::maxpower>                    _maxpower;
    _InterfaceList                                      _interfaceList;
};



template<
    class _DeviceDesc,
    class _ConfigDesc
    >
class UsbDevice
{
public:
    _DeviceDesc  devDesc;
    _ConfigDesc  configDesc;
    typedef typename _DeviceDesc::ep0size_t  ep0size_t;
    static bool   Initialize(void) {
        printf("ep0 size is %d\n",ep0size_t::v);
        return true;
    }
};






}
















#endif
